package com.yunnuo.system.comm.util;


import org.hibernate.Query;

import java.util.*;

/**
 * 负责构建HQL查询的Where条件。<br />
 * <ol>
 * <li>可以通过静态工厂方法Where.rootWhere方法创建一个不存在与其他Where实例进行逻辑And或Or操作的Where实例。</li>
 * <li>可以通过Where对象的where.andWhere工厂方法创建当前where对象的兄弟Where实例，与当前where对象的逻辑关系是And。</li>
 * <li>可以通过Where对象的where.orWhere工厂方法创建当前where对象的兄弟Where实例，与当前where对象的逻辑关系是Or。</li>
 * <li>
 * 可以通过Where对象的where.childANDWhere工厂方法创建当前where对象的子Where实例，与当前where对象的逻辑关系是And。</li>
 * <li>
 * 可以通过Where对象的where.childORWhere工厂方法创建当前where对象的子Where实例，与当前where对象的逻辑关系是or。</li>
 * </ol>
 *
 * @author songzenghui@gmail.com
 * @date 2007-11-7下午11:35:18
 */
public class Where {
    /**
     * 等于操作符(Equal)
     */
    public static final String EQ = "=";

    /**
     * 不等于操作符(Unequal)
     */
    public static final String UE = "<>";

    /**
     * 小于操作符(Less Than)
     */
    public static final String LT = "<";

    /**
     * 小于等于操作符(Less Equal)
     */
    public static final String LE = "<=";

    /**
     * 大于操作符(Greater Than)
     */
    public static final String GT = ">";

    /**
     * 大于等于操作符(Greater Equal)
     */
    public static final String GE = ">=";

    /**
     * IN 操作符
     */
    public static final String IN = "in";

    /**
     * Like 操作符
     */
    public static final String LIKE = "like";

    /**
     * IS 操作符,Hibernate 判空（非空）时使用
     */
    public static final String IS = "is";

    /**
     * And 逻辑操作符
     */
    private static final String AND = "and";

    /**
     * Or 逻辑操作符
     */
    private static final String OR = "or";

    /**
     * 表的别名
     */
    public static final String TABLE_ALIAS = "t";

    /**
     * 是否使用数据库函数
     */
    private boolean method = false;

    /**
     * @return 是否使用数据库函数
     */
    public boolean isMethod() {
        return method;
    }

    /**
     * 静态工厂方法,产生一个没有前置逻辑操作符的实例，是构建Where的起点
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @return 新的无逻辑操作符的Where实例
     */
    public static Where rootWhere(String name, String operator, Object value) {
        // 返回新的Where实例
        return new Where(name, operator, value);
    }

    /**
     * 工厂方法，产生一个当前Where条件的子条件,与当前条件的逻辑关系是And
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @return 子Where条件
     */
    public Where childANDWhere(String name, String operator, Object value) {
        // 产生一个新的Where
        Where w = new Where(name, operator, value, Where.AND);

        // 加入到子条件
        this.children.add(w);

        // 返回新生成的Where
        return w;
    }

    /**
     * 工厂方法，产生一个当前Where条件的子条件,与当前条件的逻辑关系是And
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @param isMethod
     *            是否数据函数
     *
     * @return 子Where条件
     */
    public Where childANDWhere(String name, String operator, Object value, boolean isMethod) {
        // 产生一个新的Where
        Where w = new Where(name, operator, value, Where.AND, isMethod);

        // 加入到子条件
        this.children.add(w);

        // 返回新生成的Where
        return w;
    }

    /**
     * 工厂方法，产生一个当前Where条件的子条件,与当前条件的逻辑关系是And
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @return 子Where条件
     */
    public Where childORWhere(String name, String operator, Object value) {
        // 产生一个新的Where
        Where w = new Where(name, operator, value, Where.OR);

        // 加入到子条件
        this.children.add(w);

        // 返回新生成的Where
        return w;
    }

    /**
     * 工厂方法，产生一个当前Where条件的子条件,与当前条件的逻辑关系是And
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @param isMethod
     *            是否函数
     *
     * @return 子Where条件
     */
    public Where childORWhere(String name, String operator, Object value, boolean isMethod) {
        // 产生一个新的Where
        Where w = new Where(name, operator, value, Where.OR, isMethod);

        // 加入到子条件
        this.children.add(w);

        // 返回新生成的Where
        return w;
    }

    /**
     * 工厂方法，产生一个与当前Where进行And操作的Where实例
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @return 与当前Where进行And操作的Where实例
     */
    public Where andWhere(String name, String operator, Object value) {
        // 检查是否已经存在nextWhere
        checkNextLogic(this);

        // 产生一个新的And条件
        Where w = new Where(name, operator, value, Where.AND);

        // 加入Where链条
        this.nextWhere = w;

        // 返回新生成的Where
        return w;
    }

    /**
     * 工厂方法，产生一个与当前Where进行And操作的Where实例
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @param isMethod
     *            是否函数
     *
     * @return 与当前Where进行And操作的Where实例
     */
    public Where andWhere(String name, String operator, Object value, boolean isMethod) {
        // 检查是否已经存在nextWhere
        checkNextLogic(this);

        // 产生一个新的And条件
        Where w = new Where(name, operator, value, Where.AND, isMethod);

        // 加入Where链条
        this.nextWhere = w;

        // 返回新生成的Where
        return w;
    }

    /**
     * 工厂方法，产生一个与当前Where进行Or操作的Where实例
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @return 与当前Where进行Or操作的Where实例
     */
    public Where orWhere(String name, String operator, Object value) {
        // 检查是否已经存在nextWhere
        checkNextLogic(this);

        // 产生一个新的And条件
        Where w = new Where(name, operator, value, Where.OR);

        // 加入Where链条
        this.nextWhere = w;

        // 返回新生成的Where
        return w;

    }

    /**
     * 工厂方法，产生一个与当前Where进行Or操作的Where实例
     *
     * @param name
     *            属性名
     * @param operator
     *            操作符，请使用Where的操作符常量
     * @param value
     *            操作右值
     *
     * @param isMethod
     *            是否数据库函数
     *
     *
     * @return 与当前Where进行Or操作的Where实例
     */
    public Where orWhere(String name, String operator, Object value, boolean isMethod) {
        // 检查是否已经存在nextWhere
        checkNextLogic(this);

        // 产生一个新的And条件
        Where w = new Where(name, operator, value, Where.OR, isMethod);

        // 加入Where链条
        this.nextWhere = w;

        // 返回新生成的Where
        return w;

    }

    /**
     * 设置Hibernate Query的查询参数
     *
     * @param query
     */
    @SuppressWarnings("unchecked")
    public void setQuery(Query query) {
        Map<String, Object> paramMap = this.getParameterMap();
        for (String alias : paramMap.keySet()) {
            Object value = paramMap.get(alias);
            if (value instanceof Collection) {
                query.setParameterList(alias, (Collection) value);
            } else if (value instanceof Object[]) {
                query.setParameterList(alias, (Object[])value);
            } else {
                query.setParameter(alias, value);
            }
        }

    }

    /**
     * 移除一个子条件
     *
     * @param w
     */
    public void removeChild(Where w) {
        children.remove(w);
    }

    /**
     * 清除平级的and或or条件
     */
    public void clearNextWhere() {
        this.nextWhere = null;
    }

    /**
     * 获取当前条件中所有的别名和值的Map（包括子条件）,为Query设置条件提供了便利
     *
     * @return
     */
    public Map<String, Object> getParameterMap() {
        Map<String, Object> map = new HashMap<String, Object>();
        this.fillMap(this, map);
        return map;
    }

    /**
     * 重写，产生一个where条件表达式
     */
    public String toString() {
        return " where " + this.asString();
    }

    // 形成where后的括号条件
    private String asString() {
        StringBuffer sb = new StringBuffer();
        buildWhereString(this, sb);

        return sb.toString();
    }

    // 名称
    private String name;

    // 值
    private Object value;

    // 操作符
    private String operator;

    // 当前条件与前面条件的逻辑关系，AND OR
    private String logic;

    // 内部条件
    private List<Where> children = new ArrayList<Where>();

    // 下一个Where
    private Where nextWhere;

    /**
     * 默认无前置逻辑操作符的构造方法
     *
     * @param name
     * @param operator
     * @param value
     */
    private Where(String name, String operator, Object value) {
        init(name, operator, value, null);
    }

    /**
     * 自定义设置前置逻辑操作符的构造方法
     *
     * @param name
     * @param operator
     * @param value
     * @param logic
     */
    private Where(String name, String operator, Object value, String logic) {
        init(name, operator, value, logic);
    }

    /**
     * 自定义设置前置逻辑操作符的构造方法，并判断是否使用数据库函数
     *
     * @param name
     * @param operator
     * @param value
     * @param logic
     * @param isMethod
     */
    private Where(String name, String operator, Object value, String logic, boolean isMethod) {
        this.method = isMethod;
        init(name, operator, value, logic);
    }

    // 内置的别名机制

    private String getAlias() {
        if (name == null) {
            return null;
        }
        if (this.method) {
            return "para" + Integer.toHexString(hashCode());
        } else {
            return name.replaceAll("[.]", "_") + Integer.toHexString(hashCode());
        }
    }

    // 递归构建where条件语句
    private void buildWhereString(Where w, StringBuffer sb) {

        // 设置前置逻辑操作符
        if (w.logic != null) {
            sb.append(" ");
            sb.append(w.logic);
            sb.append(" ");
        }

        // 条件括号开始

        sb.append("(");

        // 表别名 20130104 wangliang, 为解决在hibernate3.4以后中不支持连接查询，需要添加left join明码标识。
        if (!w.isMethod() && w.name.indexOf(".")==-1) {
            sb.append(Where.TABLE_ALIAS);
            sb.append(".");
        }

        // 表达式左值
        sb.append(w.name);
        sb.append(" ");

        // 操作符
        sb.append(w.operator);

        // 操作右值，使用Hibernate里的别名机制
        // 如果是IS判空（非空）操作，直接将value置入，不用别名
        if (w.operator.equals(Where.IS)) {
            sb.append(" ");
            sb.append(w.value);
            sb.append(" ");
        } else if (w.operator.equals(Where.IN)) { // IN (:alias)
            sb.append(" (:");
            sb.append(w.getAlias());
            sb.append(")");
        } else if (w.operator.equals(Where.LIKE)) { // IN (:alias)
            sb.append(" :");
            sb.append(w.getAlias());
            //TODO 需要添加特殊字符的处理，如%和_
			/*String s = (String)w.value;
			if(s.indexOf("/%")>=0 || s.indexOf("/_") >= 0){
				sb.append(" escape '/' ");
			}*/
        } else {
            sb.append(" :");
            sb.append(w.getAlias());
        }

        // 递归调用
        for (Iterator<Where> it = w.children.iterator(); it.hasNext();) {
            Where child = it.next();
            buildWhereString(child, sb);
        }

        // 如果有兄弟，则递归兄弟的

        if (w.nextWhere != null) {
            buildWhereString(w.nextWhere, sb);
        }

        // 条件括号结束
        sb.append(")");
    }

    // 递归调用，获取所有的where 别名与值的map
    private void fillMap(Where w, Map<String, Object> map) {

        // 如果是IS操作符，则无需加入Map
        if (!w.operator.equals(Where.IS)) {
            map.put(w.getAlias(), w.value);
        }

        // 递归获取子条件的参数-值Map
        for (Iterator<Where> it = w.children.iterator(); it.hasNext();) {
            Where child = it.next();
            fillMap(child, map);
        }

        // 如果还有nextWhere，再加入
        if (w.nextWhere != null) {
            fillMap(w.nextWhere, map);
        }
    }

    // 判断操作符是否合法

    private static void checkOperator(String op) {
        if (op == null
                || !(op.equals(Where.EQ) || op.equals(Where.GE) || op.equals(Where.GT) || op.equals(Where.IN)
                || op.equals(Where.LE) || op.equals(Where.LT) || op.equals(Where.UE)
                || op.equals(Where.LIKE) || op.equals(Where.IS))) {
            throw new Error("错误的Where操作符，请使用Where类的操作符常量");
        }
    }

    // 判断逻辑操作符是否合法

    private static void checkLogic(String logic) {

        if (logic != null && !(logic.equals(Where.AND) || logic.equals(Where.OR))) {
            throw new Error("错误的Where操作符，请使用Where类的操作符常量");
        }
    }

    // 判断是否存在平级（nextWhere）的where 条件
    private static void checkNextLogic(Where w) {
        if (w.nextWhere != null) {
            throw new Error("已存在nextWhere，不允许再设置另一个nextWhere");
        }
    }

    // 初始化

    private void init(String name, String operator, Object value, String logic) {

        checkLogic(logic);
        checkOperator(operator);

        this.name = name;
        this.operator = operator;
        this.value = value;
        this.logic = logic;

        // 判断，如果操作符为like，则将value转换为String，并自动添加%（如果已经有了则不加）

        if (Where.LIKE.equals(operator)) {
            String tmp = value.toString();
		 if (tmp.indexOf('%') < 0 && tmp.indexOf("_") < 0) {
                tmp = "%" + tmp + "%";
            }
            this.value = tmp = "%" + tmp + "%";
        }
    }

}